import { kebabCase } from 'lodash';
import path from 'path';
import type { Plugin } from 'rollup';
import type { FilterPattern } from '@rollup/pluginutils';
import { createFilter } from '@rollup/pluginutils';
import { slash } from '../utils';

export interface EsbuildPluginImportOption {
  libraryName: string;
  /**
   * @default 'es'
   */
  libraryDirectory?: string;
  style?: true | 'css' | ((importName: string, importPath: string) => string);
  customStyleName?: string;
  /**
   * @default true
   */
  camel2DashComponentName?: boolean;
  /**
   * @default true
   */
  transformToDefaultImport?: boolean;
  ignoreImports?: (RegExp | string)[];
  include?: FilterPattern;
  exclude?: FilterPattern;
}

const importReg = /^import\s+{((?:.|\n)*?)}\s+from\s+['"](.*?)['"];?/m;

const generateImportExpression = (
  importExpressionInfo: {
    importExpression: string;
    memberString: string;
    libraryString: string;
  },
  config: EsbuildPluginImportOption,
) => {
  const { memberString, libraryString } = importExpressionInfo;

  const {
    libraryDirectory = 'es',
    camel2DashComponentName = true,
    style,
    ignoreImports,
    transformToDefaultImport = true,
  } = config;

  const importLines = [];
  const members = memberString
    // ignore comments //
    .replace(/\/\/.*/gm, '')
    // ignore comments /* */
    .replace(/\/\*(.|\n)*?\*\//gm, '')
    .split(',')
    .map((v) => v.replace(/(^\s+|\s+$)/g, ''))
    .filter(Boolean);

  const ignoreImportNames = [];

  // eslint-disable-next-line no-restricted-syntax
  for (const member of members) {
    const [rawMemberName, aliasMemberName] = member.split(/\s+as\s+/);
    const memberName = aliasMemberName || rawMemberName;

    if (ignoreImports?.length) {
      const isIgnore = ignoreImports.some((ignoreReg) => {
        if (typeof ignoreReg === 'string') {
          return ignoreReg === rawMemberName;
        }
        return ignoreReg.test(rawMemberName);
      });
      if (isIgnore) {
        ignoreImportNames.push(member);
        // eslint-disable-next-line no-continue
        continue;
      }
    }

    const transformedMemberName = camel2DashComponentName
      ? kebabCase(rawMemberName)
      : rawMemberName;

    const memberImportDirectory = path.join(libraryString, libraryDirectory, transformedMemberName);

    let stylePath = memberImportDirectory;

    if (style === true) {
      stylePath = path.join(stylePath, 'style');
      importLines.push(`import "${stylePath}";`);
    } else if (style === 'css') {
      stylePath = path.join(stylePath, 'style', 'css');
      importLines.push(`import "${stylePath}";`);
    } else if (typeof style === 'function') {
      stylePath = style(rawMemberName, stylePath);
      importLines.push(`import "${stylePath}";`);
    }

    if (transformToDefaultImport) {
      importLines.push(`import ${memberName} from "${memberImportDirectory}";`);
    } else {
      importLines.push(`import {${memberName}} from "${memberImportDirectory}";`);
    }
  }

  if (ignoreImportNames.length) {
    importLines.push(`import {${ignoreImportNames.join(',')}} from "${libraryString}";`);
  }

  return importLines.map((line) => slash(line)).join('\n');
};

const generateNewContent = (content: string, config: EsbuildPluginImportOption) => {
  let newContent = '';
  let matchContent = content;

  // eslint-disable-next-line no-constant-condition
  while (true) {
    const matches = importReg.exec(matchContent);

    if (!matches) {
      break;
    }
    const [importExpression, memberString, libraryString] = matches;
    if (libraryString === config.libraryName) {
      newContent += matchContent.substring(0, matches.index);

      newContent += generateImportExpression(
        { importExpression, memberString, libraryString },
        config,
      );
    } else {
      newContent += matchContent.substring(0, matches.index + importExpression.length);
    }

    matchContent = matchContent.substring(
      matches.index + importExpression.length,
      matchContent.length,
    );
  }
  newContent += matchContent;

  return newContent;
};

export default function importPlugin(options: EsbuildPluginImportOption): Plugin {
  const filter = createFilter(options.include, options.exclude);

  return {
    name: 'seepln:import',

    transform(code, id) {
      if (!filter(id)) {
        return null;
      }

      if (!code.includes(options.libraryName)) {
        return null;
      }

      const content = generateNewContent(code, options);

      return {
        code: content,
        map: null,
      };
    },
  };
}
